import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.ReferenceCountUtil;
import org.junit.Test;

/**
 * Created with IntelliJ IDEA.
 * User: xinrong3
 * Date: 14-4-2
 * Time: 上午10:05
 */
public class testNetty {

    public void testServer() {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //初始化channel的辅助类，为具体子类提供公共数据结构
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup,workerGroup);
        bootstrap.channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new HelloWorldServerHandler());
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

        // Bind and start to accept incoming connections.
        ChannelFuture f = null; // (7)
        try {
            f = bootstrap.bind(8080).sync();
            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }



    public void testClient() {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        //创建客户端channel的辅助类,发起connection请求
        Bootstrap b = new Bootstrap();
        //It means one same HelloWorldClientHandler instance is going to handle multiple Channels and consequently the data will be corrupted.
        //基于上面这个描述，必须用到ChannelPipelineFactory每次创建一个pipeline
        b.group(workerGroup);
        b.channel(NioSocketChannel.class); // (3)
        b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
        b.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new HelloWorldClientHandler(10));
            }
        });

        // Wait until the connection is closed.
        try {
            ChannelFuture f = b.connect("127.0.0.1", 8080).sync(); // (5)
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            workerGroup.shutdownGracefully();
        }
    }


    @Test
    public void testNetty() throws InterruptedException {
        System.out.println("start server");
        Thread t=new Thread(){
            @Override
            public void run(){
                testServer();
            }
        };
        t.start();

        Thread.sleep(100);

        System.out.println("start client");
        Thread tc=new Thread(){
            @Override
            public void run(){
                testClient();
            }
        };
        tc.start();
        while (true){}
    }


    public class HelloWorldServerHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object e)
                throws Exception {
            ByteBuf in = (ByteBuf) e;
            try {
                while (in.isReadable()) { // (1)
                    System.out.print((char) in.readByte());
                    System.out.flush();
                }
            } finally {
                ReferenceCountUtil.release(e); // (2)
            }
        }

        public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
            System.out.println("Unexpected exception from downstream."
                    + e.getCause());
            e.printStackTrace();
            ctx.close();
        }
    }

    class HelloWorldClientHandler extends ChannelInboundHandlerAdapter {


        private final ByteBuf firstMessage;

        HelloWorldClientHandler(int firstMessageSize){
            if (firstMessageSize <= 0) {
                throw new IllegalArgumentException("firstMessageSize: " + firstMessageSize);
            }
            firstMessage = Unpooled.buffer(firstMessageSize);
            for (int i = 0; i < firstMessage.capacity(); i ++) {
                firstMessage.writeByte((byte) i);
            }
        }


        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            String hellow="hellow";
            ByteBuf byteBuf=Unpooled.buffer(hellow.length());
            ctx.writeAndFlush(byteBuf.writeBytes(hellow.getBytes()));
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ctx.write(msg);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.flush();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // Close the connection when an exception is raised.
            ctx.close();
        }
    }
}
